﻿using System;
using System.Threading;
using UtilZ.Dotnet.Ex.AsynWait;
using UtilZ.Dotnet.Ex.ZLog;

namespace UtilZ.Dotnet.WindowsDesktopEx.Base
{
    /// <summary>
    /// 执行异步等待基类
    /// </summary>
    /// <typeparam name="T">异步执行参数类型</typeparam>
    /// <typeparam name="TContainer">容器控件类型</typeparam>
    /// <typeparam name="TResult">异步执行返回值类型</typeparam>
    public abstract class AsynExcuteAbs<T, TContainer, TResult> : PartAsynWaitAbs, IAsynExcute<T, TContainer, TResult>, IAsynExcuteCancell where TContainer : class
    {
        /// <summary>
        /// 异步执行线程
        /// </summary>
        private Thread _asynExcuteThread = null;

        /// <summary>
        /// 异步执行线程取消对象
        /// </summary>
        private CancellationTokenSource _asynExcuteThreadCts = null;

        /// <summary>
        /// 异步等待执行参数
        /// </summary>
        protected PartAsynWaitPara<T, TResult> _asynWaitPara = null;

        /// <summary>
        /// 是否已执行完成
        /// </summary>
        private bool _excuteCompleted = false;
        private readonly object _excuteCompletedLock = new object();

        /// <summary>
        /// 获取包含有关控件的数据的对象
        /// </summary>
        public object Tag
        {
            get
            {
                var asynWaitPara = _asynWaitPara;
                return asynWaitPara == null ? null : asynWaitPara.Tag;
            }
        }



        /// <summary>
        /// 构造函数
        /// </summary>
        protected AsynExcuteAbs()
            : base()
        {

        }



        /// <summary>
        /// 容器控件
        /// </summary>
        protected TContainer _containerControl;

        /// <summary>
        /// 异步执行时新new的等待控件
        /// </summary>
        private IPartAsynWait _excuteNewWaitShadeControl = null;

        /// <summary>
        /// 设置等待遮罩层控件
        /// </summary>
        /// <param name="asynWaitPara"></param>
        private void SetWaitShadeControl(PartAsynWaitPara<T, TResult> asynWaitPara)
        {
            IPartAsynWait waitShadeControl = asynWaitPara.AsynWait;
            if (waitShadeControl == null)
            {
                IShadeControlFactory shadeControlFactory = base.GetShadeControlFactory();
                if (shadeControlFactory == null)
                {
                    throw new Exception("没有指定异步等待遮罩层工厂对象");
                }

                if (asynWaitPara.ShadeControlType == ShadeControlType.Full)
                {
                    waitShadeControl = shadeControlFactory.CreateShadeFullControl();
                }
                else
                {
                    waitShadeControl = shadeControlFactory.CreateShadeAnimalControl();
                }

                PartAsynUIParaProxy.SetAsynWait(asynWaitPara, waitShadeControl);
                this._excuteNewWaitShadeControl = waitShadeControl;
            }

            waitShadeControl.ShadeOpacity = asynWaitPara.Opacity;
            waitShadeControl.Title = asynWaitPara.Title;
            waitShadeControl.Message = asynWaitPara.Message;
            waitShadeControl.ShowCancel = asynWaitPara.ShowCancel;
        }

        /// <summary>
        /// 执行异步委托
        /// </summary>
        /// <param name="asynWaitPara">异步等待执行参数</param>
        /// <param name="containerControl">容器控件</param>
        public void Excute(PartAsynWaitPara<T, TResult> asynWaitPara, TContainer containerControl)
        {
            lock (asynWaitPara.SyncRoot)
            {
                if (asynWaitPara.Islock)
                {
                    throw new ArgumentException($"异步参数已处于执行中,不可重复执行");
                }

                PartAsynUIParaProxy.Lock(asynWaitPara);
            }

            this._asynWaitPara = asynWaitPara;

            this._containerControl = containerControl;
            this.SetWaitShadeControl(asynWaitPara);

            this.ExcutePre(asynWaitPara, containerControl);
            asynWaitPara.AsynWait.SetFocus();

            //启动执行线程
            lock (this._excuteCompletedLock)
            {
                AsynExcuteCancellHelper.AddAsynExcuteCancell(this);
                this._excuteCompleted = false;

                //取消执行委托
                this._asynWaitPara.AsynWait.CanceledNotify += this.CanceledNotify;

                //启动等待条动画
                this._asynWaitPara.AsynWait.StartAnimation();

                this._asynExcuteThreadCts = new CancellationTokenSource();
                this._asynExcuteThread = new Thread(this.AsynExcuteThreadMethod);
                this._asynExcuteThread.IsBackground = true;
                this._asynExcuteThread.Name = "UI异步执行线程";
                this._asynExcuteThread.Start();
            }
        }


        /// <summary>
        /// 执行准备
        /// </summary>
        /// <param name="asynWaitPara"></param>
        /// <param name="containerControl"></param>
        protected abstract void ExcutePre(PartAsynWaitPara<T, TResult> asynWaitPara, TContainer containerControl);


        /// <summary>
        /// UI异步执行线程方法
        /// </summary>
        private void AsynExcuteThreadMethod()
        {
            TResult result = default(TResult);
            PartAsynExcuteStatus excuteStatus;
            Exception excuteEx = null;
            try
            {
                var token = this._asynExcuteThreadCts.Token;
                var function = this._asynWaitPara.Function;
                if (function != null)
                {
                    result = function(new PartAsynFuncPara<T>(this._asynWaitPara.Para, token, this._asynWaitPara.AsynWait));
                }

                if (token.IsCancellationRequested)
                {
                    excuteStatus = PartAsynExcuteStatus.Cancel;
                }
                else
                {
                    excuteStatus = PartAsynExcuteStatus.Completed;
                }
            }
            catch (Exception ex)
            {
                excuteStatus = PartAsynExcuteStatus.Exception;
                excuteEx = ex;
            }

            lock (this._excuteCompletedLock)
            {
                if (this._excuteCompleted)
                {
                    return;
                }
                this._excuteCompleted = true;

                this.ExcuteCompleted(result, excuteStatus, excuteEx);
            }
        }

        private void ExcuteCompleted(TResult result, PartAsynExcuteStatus excuteStatus, Exception excuteEx)
        {
            object[] args = new object[] { result, excuteStatus, excuteEx };
            var containerControl = this._containerControl;
            if (containerControl is System.Windows.Forms.Control)
            {
                (containerControl as System.Windows.Forms.Control).Invoke(new Action<TResult, PartAsynExcuteStatus, Exception>(this.PrimitiveExcuteCompleted), args);
            }
            else if (containerControl is System.Windows.Threading.DispatcherObject)
            {
                (containerControl as System.Windows.Threading.DispatcherObject).Dispatcher.Invoke(new Action<TResult, PartAsynExcuteStatus, Exception>(this.PrimitiveExcuteCompleted), args);
            }
            else
            {
                throw new NotSupportedException($"不支持的控件类型:{containerControl.GetType().FullName}");
            }
        }

        private void PrimitiveExcuteCompleted(TResult result, PartAsynExcuteStatus excuteStatus, Exception excuteEx)
        {
            //释放资源
            this.ReleaseResource();

            //执行完成委托
            var completedAction = this._asynWaitPara.Completed;
            if (completedAction != null)
            {
                var asynExcuteResult = new PartAsynExcuteResult<T, TResult>(this._asynWaitPara.Para, excuteStatus, result, excuteEx);
                completedAction(asynExcuteResult);
            }

            this._containerControl = null;

            //必须放在执行完成之后,因为在完成委托中可能会用上该对象            
            if (this._excuteNewWaitShadeControl != null)
            {
                this._excuteNewWaitShadeControl.Dispose();
                this._excuteNewWaitShadeControl = null;
            }
        }


        /// <summary>
        /// 取消执行
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void CanceledNotify(object sender, EventArgs e)
        {
            this.PrimitiveCancell();
        }

        /// <summary>
        /// 取消执行
        /// </summary>
        public void Cancell()
        {
            this.PrimitiveCancell();
        }

        /// <summary>
        /// 取消执行
        /// </summary>
        private void PrimitiveCancell()
        {
            lock (this._excuteCompletedLock)
            {
                if (this._excuteCompleted)
                {
                    return;
                }

                try
                {
                    var cts = this._asynExcuteThreadCts;
                    if (cts == null ||
                        cts != null && cts.Token.IsCancellationRequested)
                    {
                        return;
                    }

                    cts.Cancel();

                    //取消通知
                    var cancelHandle = this._asynWaitPara.Cancel;
                    if (cancelHandle != null)
                    {
                        cancelHandle(this._asynWaitPara.Para);
                    }
                }
                catch (ObjectDisposedException)
                {
                    return;
                }

                if (this._asynExcuteThread != null && this._asynWaitPara.ImmediatelyCompleted)
                {
                    this._asynExcuteThread = null;
                    this.ExcuteCompleted(default(TResult), PartAsynExcuteStatus.Cancel, null);
                    this._excuteCompleted = true;
                }
            }
        }



        /// <summary>
        /// 释放异步委托资源
        /// </summary>
        private void ReleaseResource()
        {
            try
            {
                PartAsynUIParaProxy.UnLock(this._asynWaitPara);
                this._asynWaitPara.AsynWait.CanceledNotify -= CanceledNotify;
                this._asynWaitPara.AsynWait.StopAnimation();
                AsynExcuteCancellHelper.RemoveAsynExcuteCancell(this);
                this._asynExcuteThreadCts.Dispose();
                this._asynExcuteThreadCts = null;

                this.ExcuteFinished();

                if (this._asynWaitPara.AutoDisposeAsynWait)
                {
                    this._asynWaitPara.AsynWait.Dispose();
                }
            }
            catch (Exception ex)
            {
                ZLoger.Error(ex);
            }
        }

        /// <summary>
        /// 执行结束
        /// </summary>
        protected abstract void ExcuteFinished();
    }
}
